package com.atguigu.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.common.to.es.SpuUpTo;
import com.atguigu.search.config.SearchConfiguation;
import com.atguigu.search.constant.ProductConstant;
import com.atguigu.search.service.SearchSaveService;
import com.atguigu.search.vo.SearchParam;
import com.atguigu.search.vo.SearchResult;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.NestedQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class SearchSaveServiceImpl implements SearchSaveService {

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    /**
     * 保存数据到es中
     *
     * @param spuUpTos
     * @throws IOException
     */
    public void searchSave(List<SpuUpTo> spuUpTos) throws IOException {

        //定义批量保存es数据
        BulkRequest bulkRequest = new BulkRequest();
        //保存数据到es中
        for (SpuUpTo spuUpTo : spuUpTos) {
            //构造保存请求
            IndexRequest request = new IndexRequest(SearchParam.INDEX);
            request.id(spuUpTo.getSkuId().toString());
            //将数据保存为json格式
            String jsonString = JSON.toJSONString(spuUpTo);

            request.source(jsonString, XContentType.JSON);
            bulkRequest.add(request);

        }


        //发送请求保存es数据
        restHighLevelClient.bulk(bulkRequest, SearchConfiguation.COMMON_OPTIONS);


    }

    /**
     * 完成数据的检索
     *
     * @param searchParam
     */
    public SearchResult search(SearchParam searchParam) {
        SearchResult result = null;
        //构建检索的查询封装
        SearchRequest request = buildSearchRequest(searchParam);


        try {
            //执行检索
            SearchResponse response = restHighLevelClient.search(request, SearchConfiguation.COMMON_OPTIONS);
            //处理检索数据
            result = buildSearchBuilder(searchParam, response);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 请求数据的封装
     *
     * @param searchParam
     * @return
     */
    public SearchRequest buildSearchRequest(SearchParam searchParam) {
        //构建dsl语句
        SearchSourceBuilder builder = new SearchSourceBuilder();
        //构建bool查询条件
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        //当前端keyword不为空的时候，将其作为查询条件
        if (searchParam.getKeyword() != null) {
            boolQuery.must(QueryBuilders.matchQuery("skuTitle", searchParam.getKeyword()));
        }
        //当前端分类id不为空的时候，将其作为查询条件
        if (searchParam.getCatalog3Id() != null) {
            boolQuery.filter(QueryBuilders.termQuery("catalogId", searchParam.getCatalog3Id()));
        }
        //当前端品牌id不为空的时候，将其作为查询条件
        if (searchParam.getBrandId() != null && searchParam.getBrandId().size() > 0) {
            boolQuery.filter(QueryBuilders.termsQuery("brandId", searchParam.getBrandId()));
        }
        if (searchParam.getHasStock() != null) {
            //对有无库存来进行检索
            boolQuery.filter(QueryBuilders.termQuery("hasStock", searchParam.getHasStock() == 1 ? true : false));
        }


        //当前端价格不为空的时候，将其作为查询条件
        if (searchParam.getSkuPrice() != null) {
            RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("skuPrice");
            String[] split = searchParam.getSkuPrice().split("_");
            if (split.length == 1) {
                //说明是600_这种格式
                rangeQueryBuilder.gte(split[0]);
            } else if (split.length == 2 && "".equals(split[0])) {
                //说明是_600这种格式
                rangeQueryBuilder.lte(split[1]);
            } else {
                //说明是600_1000这种格式
                rangeQueryBuilder.gte(split[0]).lte(split[1]);
            }
            boolQuery.filter(rangeQueryBuilder);
        }

        //当属性对应的值不为空时--将其作为查询条件
        if (searchParam.getAttrs() != null && searchParam.getAttrs().size() > 0) {
            //这边需要注意的一点是，如果真的存在多个属性的查询，是有多个filter条件的
            for (String attr : searchParam.getAttrs()) {
                //attr=1_5寸:8寸&attrs=2_16G:8G
                String[] split = attr.split("_"); //分隔成1和5寸:8寸
                String[] split1 = split[1].split(":"); //收集成5寸,8寸
                //构建查询条件
                BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
                boolQueryBuilder.must(QueryBuilders.termQuery("attrs.attrId", split[0]));
                boolQueryBuilder.must(QueryBuilders.termsQuery("attrs.attrValue", split1));
                NestedQueryBuilder nestedQuery = QueryBuilders.nestedQuery("attrs", boolQueryBuilder, ScoreMode.None);//None表示不参与评分
                boolQuery.filter(nestedQuery);
            }

        }
        //封装所有的查询条件
        builder.query(boolQuery);


        //添加排序条件
        if (searchParam.getSort() != null) {
            //根据指定的条件添加排序
            //sort=hotScore_asc/desc
            String sort = searchParam.getSort();
            String[] split = sort.split("_");
            builder.sort(split[0], "asc".equalsIgnoreCase(split[1]) ? SortOrder.ASC : SortOrder.DESC);
        }

        //分页
        //当前页码
        builder.from((searchParam.getPageNum() - 1) * SearchParam.page);
        builder.size(SearchParam.page); //写死

        //高亮显示数据
        if (searchParam.getKeyword() != null) {
            //添加高亮数据显示
            HighlightBuilder highlighter = new HighlightBuilder();
            highlighter.field("skuTitle");
            highlighter.preTags("<b style='color:red'>");
            highlighter.postTags("</b>");
            builder.highlighter(highlighter);
        }

        //封装聚合查询
        //按照品牌聚合
        TermsAggregationBuilder termsAggregationBuilder = AggregationBuilders.terms("brand_agg").field("brandId").size(10);
        termsAggregationBuilder.subAggregation(AggregationBuilders.terms("brand_name_agg").field("brandName").size(1));
        termsAggregationBuilder.subAggregation(AggregationBuilders.terms("brand_name_img").field("brandImg").size(1));
        builder.aggregation(termsAggregationBuilder);

        //按照分类聚合
        TermsAggregationBuilder aggregationBuilder = AggregationBuilders.terms("catalog_agg").field("catalogId").size(10);
        aggregationBuilder.subAggregation(AggregationBuilders.terms("catalog_name_agg").field("catalogName").size(1));
        builder.aggregation(aggregationBuilder);

        //按照属性聚合
        NestedAggregationBuilder nestedAggregationBuilder = AggregationBuilders.nested("attr_agg", "attrs");
        //根据属性id来聚合
        TermsAggregationBuilder attrIdAgg = AggregationBuilders.terms("attr_id_agg").field("attrs.attrId").size(10);
        attrIdAgg.subAggregation(AggregationBuilders.terms("attr_name_agg").field("attrs.attrName").size(1));
        attrIdAgg.subAggregation(AggregationBuilders.terms("attr_value_agg").field("attrs.attrValue").size(10));

        //将属性聚合加入到NestedAggregationBuilder中
        nestedAggregationBuilder.subAggregation(attrIdAgg);
        //总体加入到SearchSourceBuilder中
        builder.aggregation(nestedAggregationBuilder);

        System.out.println(builder);
        SearchRequest searchRequest = new SearchRequest(new String[]{SearchParam.INDEX}, builder);
        return searchRequest;
    }

    /**
     * 响应数据的封装
     *
     * @param searchParam
     * @param response
     * @return
     */
    public SearchResult buildSearchBuilder(SearchParam searchParam, SearchResponse response) {
        //设置分类集合
        List<SearchResult.CatalogVo> catalogVos = new ArrayList<>();

        //设置品牌集合
        List<SearchResult.BrandVo> brandVos = new ArrayList<>();

        //设置属性集合
        List<SearchResult.AttrVo> attrVos = new ArrayList<>();

        System.out.println(response);
        SearchResult result = new SearchResult();
        //设置三级分类信息
        if (response.getAggregations() != null) {
            ParsedLongTerms aggregations = response.getAggregations().get("catalog_agg");
            List<? extends Terms.Bucket> buckets = aggregations.getBuckets();
            for (Terms.Bucket bucket : buckets) {
                SearchResult.CatalogVo catalogVo = new SearchResult.CatalogVo();
                catalogVo.setCatalogId(bucket.getKeyAsNumber().longValue());
                //设置分类的名称
                ParsedStringTerms stringTerms = bucket.getAggregations().get("catalog_name_agg");
                catalogVo.setCatalogName(stringTerms.getBuckets().get(0).getKeyAsString());

                //加入到分类集合中
                catalogVos.add(catalogVo);
            }
            result.setCatalogs(catalogVos);


            //设置品牌信息
            ParsedLongTerms parsedLongTerms = response.getAggregations().get("brand_agg");
            for (Terms.Bucket bucket : parsedLongTerms.getBuckets()) {
                SearchResult.BrandVo brandVo = new SearchResult.BrandVo();
                brandVo.setBrandId(bucket.getKeyAsNumber().longValue());
                ParsedStringTerms stringTerms = bucket.getAggregations().get("brand_name_img");
                brandVo.setBrandImg(stringTerms.getBuckets().get(0).getKeyAsString());
                ParsedStringTerms parsedStringTerms = bucket.getAggregations().get("brand_name_agg");
                brandVo.setBrandName(parsedStringTerms.getBuckets().get(0).getKeyAsString());
                brandVos.add(brandVo);
            }
            result.setBrands(brandVos);


            //  设置属性集合
            ParsedNested nested = response.getAggregations().get("attr_agg");
            ParsedLongTerms attrIdAgg = nested.getAggregations().get("attr_id_agg");
            for (Terms.Bucket bucket : attrIdAgg.getBuckets()) {
                SearchResult.AttrVo attrVo = new SearchResult.AttrVo();
                attrVo.setAttrId(bucket.getKeyAsNumber().longValue());
                //取出对应的属性名称
                ParsedStringTerms parsedStringTerms = bucket.getAggregations().get("attr_name_agg");
                attrVo.setAttrName(parsedStringTerms.getBuckets().get(0).getKeyAsString());
                //取出对应的属性值
                ParsedStringTerms terms = bucket.getAggregations().get("attr_value_agg");
                List<String> stringList = terms.getBuckets().stream().map((m) -> {
                    String keyAsString = ((Terms.Bucket) m).getKeyAsString();
                    return keyAsString;
                }).collect(Collectors.toList());

                attrVo.setAttrValue(stringList);
                attrVos.add(attrVo);
            }
            result.setAttrs(attrVos);
        }


        //设置命中的商品信息
        //封装对象返回商品信息
        List<SpuUpTo> spuUpTos = new ArrayList<>();
        if (response.getHits().getHits() != null && response.getHits().getHits().length > 0) {
            //说明hits中的不为空
            SearchHit[] hits = response.getHits().getHits();
            for (SearchHit hit : hits) {
                String sourceAsString = hit.getSourceAsString();
                SpuUpTo spuUpTo = JSON.parseObject(sourceAsString, SpuUpTo.class);
                if(searchParam.getKeyword()!=null){
                    //设置高亮显示
                    String skuTitle = hit.getHighlightFields().get("skuTitle").getFragments()[0].toString();
                    spuUpTo.setSkuTitle(skuTitle);
                }
                spuUpTos.add(spuUpTo);
            }
        }
        result.setProducts(spuUpTos);


        //设置当前页码
        result.setPageNum(searchParam.getPageNum());
        //设置总记录数--这里指的是命中的总记录数
        long value = response.getHits().getTotalHits().value;
        result.setTotal(response.getHits().getTotalHits().value);
        //设置总页码
        int yushu = (int) value % SearchParam.page;
        result.setTotalPages(yushu == 0 ? (int) value / SearchParam.page : ((int) value / SearchParam.page + 1));



        List<Integer> pageNavs = new ArrayList<>();
        for (int i=1;i<=result.getTotalPages();i++){
            pageNavs.add(i);
        }
        result.setPageNavs(pageNavs);

        return result;
    }


}


